package com.rent.appointment.service.serviceImpl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.rent.appointment.dao.ApplyRentDao;
import com.rent.appointment.dto.ApplyRentDto;
import com.rent.appointment.entity.ApplyRent;
import com.rent.appointment.service.ApplyService;
import com.rent.common.constant.CommonConstant;
import com.rent.common.entity.ApplyMessageDto;
import com.rent.common.exception.BusinessException;
import com.rent.common.interceptor.UserContext;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;

@Service
public class ApplyServiceImpl extends ServiceImpl<ApplyRentDao, ApplyRent> implements ApplyService {

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private ApplyRentDao applyRentDao;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 申请锁前缀
     */
    public static final String LOCK_APPLY_PREFIX = "lock:apply:";

    @Override
    public void createApply(ApplyRentDto applyRentDto) {
        //创建实体类，把dto转成entity
        ApplyRent applyRent = new ApplyRent();
        BeanUtils.copyProperties(applyRentDto,applyRent);

        Long userId = UserContext.getUserId();
        Long houseId = applyRent.getHouseId();

        String lockKey = LOCK_APPLY_PREFIX + userId + ":" + houseId;

        RLock lock = redissonClient.getLock(lockKey);
        try {
            if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {
                // 检查是否已预约
                ApplyRent existApply = getBaseMapper().selectByUserIdAndHouseId(userId, houseId);
                if (existApply != null) {
                    throw new BusinessException("您已经申请过该房源");
                }

                // 创建预约
                applyRent.setUserId(userId);
                applyRentDao.insert(applyRent);

                //发送消息通知房东
                ApplyMessageDto amDto = ApplyMessageDto.builder()
                        .id(applyRent.getId())
                        .userId(applyRent.getUserId())
                        .houseId(applyRent.getHouseId())
                        .landlordId(applyRent.getLandlordId())
                        .status(applyRent.getStatus())
                        .createTime(applyRent.getCreateTime())
                        .updateTime(applyRent.getUpdateTime())
                        .contactPhone(applyRent.getContactPhone())
                        .handleTime(applyRent.getHandleTime())
                        .build();

                rabbitTemplate.convertAndSend(
                        CommonConstant.EXCHANGE_NAME,
                        CommonConstant.ROUTING_KEY_APPLY,
                        amDto
                );

            } else {
                throw new BusinessException("请勿重复提交");
            }
        } catch (InterruptedException e) {
            throw new BusinessException("提交申请失败");
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    @Override
    public void deleteRent(Long id) {
        //通过id获取实体类
        ApplyRent applyRent = getById(id);

        Long userId = UserContext.getUserId();
        Long houseId = applyRent.getHouseId();
        String lockKey = LOCK_APPLY_PREFIX + userId + ":" + houseId;

        RLock lock = redissonClient.getLock(lockKey);
        try {
            if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {

                //如果房东未处理不通知房东，如果房东处理了 通知房东
                // 检查是否已申请
                ApplyRent existApply = getBaseMapper().selectByUserIdAndHouseId(userId, houseId);
                if (null == existApply) {
                    throw new BusinessException("该申请不存在！");
                }
                // 检查是否已经处理申请
                if (3 == existApply.getStatus()){
                    throw  new BusinessException("申请已取消！");
                }else if(1 == existApply.getStatus() || 2 == existApply.getStatus()){
                    // 更新申请信息（其实应该是把status更新为3）
                    applyRent.setStatus(3);
                    applyRentDao.updateById(applyRent);

                    //发送消息通知房东，房东处理过预约请求，取消预约理应通知房东
                    ApplyMessageDto amDto = ApplyMessageDto.builder()
                            .id(applyRent.getId())
                            .userId(applyRent.getUserId())
                            .houseId(applyRent.getHouseId())
                            .landlordId(applyRent.getLandlordId())
                            .status(applyRent.getStatus())
                            .createTime(applyRent.getCreateTime())
                            .updateTime(applyRent.getUpdateTime())
                            .contactPhone(applyRent.getContactPhone())
                            .handleTime(applyRent.getHandleTime())
                            .build();

                    rabbitTemplate.convertAndSend(
                            CommonConstant.EXCHANGE_NAME,
                            CommonConstant.ROUTING_KEY_APPLY,
                            amDto
                    );
                    //消息通知完再删除
                    // 取消预约信息(在表里面删除)
                    applyRentDao.deleteById(applyRent);

                }else if (0 == existApply.getStatus()){
                    //房东尚未处理消息，租客取消预约不用通知房东
                    // 更新预约信息（其实应该是把status更新为3）
                    applyRent.setStatus(3);
                    applyRentDao.updateById(applyRent);

                    // 取消预约信息(在表里面删除)
                    applyRentDao.deleteById(applyRent);
                }

            } else {
                throw new BusinessException("请先创建申请");
            }
        } catch (InterruptedException e) {
            throw new BusinessException("取消申请失败");
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    //使用消息队列处理预约消息（先用分布式锁改状态，再用消息队列发通知）
    @Override
    public void handleApply(Long Id, Integer status) {
        //直接从这里把错误传参处理 如果传的status值为0 返回
        //判断是否处理成功（有可能传的status值是0）
        if (0 == status){
            throw  new BusinessException("申请处理失败，请选择同意或是拒绝！");
        }else {
            //分布式锁更新状态方法
            //先通过id获取applyRent对象
            ApplyRent applyRent = applyRentDao.selectById(Id);
            //userContext获取的是当前登陆用户，也就是说正在处理预约的房东id
            Long landlordId = UserContext.getUserId();

            String lockKey = LOCK_APPLY_PREFIX + landlordId + ":" + Id;

            RLock lock = redissonClient.getLock(lockKey);
            try {
                if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {
                    // 检查申请是否存在(这里是通过id拿到的对象，理论上应该存在，不然走不到这里）
                    ApplyRent existApply = applyRentDao.selectById(Id);
                   /* if (null == existApply) {
                        throw new BusinessException("该申请不存在！");
                    }*/
                    // 检查是否已经处理申请
                    if (3 == existApply.getStatus()){
                        throw  new BusinessException("申请已取消！");
                    }else if(1 == existApply.getStatus() || 2 == existApply.getStatus()){
                        throw  new BusinessException("申请已处理，请勿重复处理！");
                    }

                    // 更新申请信息
                    applyRent.setStatus(status);
                    applyRentDao.updateById(applyRent);
                } else {
                    throw new BusinessException("请先创建申请！");
                }
            } catch (InterruptedException e) {
                throw new BusinessException("处理申请失败！");
            } finally {
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }

            //发送消息通知房东
            ApplyMessageDto amDto = ApplyMessageDto.builder()
                    .id(applyRent.getId())
                    .userId(applyRent.getUserId())
                    .houseId(applyRent.getHouseId())
                    .landlordId(applyRent.getLandlordId())
                    .status(applyRent.getStatus())
                    .createTime(applyRent.getCreateTime())
                    .updateTime(applyRent.getUpdateTime())
                    .contactPhone(applyRent.getContactPhone())
                    .handleTime(applyRent.getHandleTime())
                    .build();

            rabbitTemplate.convertAndSend(
                    CommonConstant.EXCHANGE_NAME,
                    CommonConstant.ROUTING_KEY_APPLY,
                    amDto
            );
        }
    }
}
